<?php
/**
 * Date: 2018/6/27
 * Time: 19:48
 * Author: zeronlee
 */

namespace App\Models\ProjectManage;


use App\Models\Flaw\Level;
use App\Models\Ogz;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use App\Models\ProjectManage\Traits\Relationship\ProjectManageRelationship as Pr;
use App\Models\ProjectManage\Traits\AppRelate;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Cache;
use App\Models\Access\User\User;
use App\Models\App;
use App\Models\Flaw\Vuls;

class ProjectTask extends Model
{
    use Pr;
    use AppRelate;

    protected $table = 'project_task';

    protected $primaryKey = 'id';

    protected $fillable = [];

    protected $guarded = ['app_id'];

    public $timestamps = true;

    const INTENDING  = 1;
    const PROCESSING = 2;
    const COMPLETED  = 3;
    const DELETED    = 4;

    public $requestWhere = [];

    private $builder;

    public static $status = [self::INTENDING=>'计划中',self::PROCESSING=>'进行中',self::COMPLETED=>'已完成',self::DELETED=>'已删除'];

    /**
     * 插入一条记录
     * @param $request
     */
    public function addRow ($request){
        $data = $request->input();
        $data['pro_number'] = $this->generateNumber();
        $data['user_id']    = Auth::user()->id;
        $data['started_at'] = Carbon::parse($request->started_at);
        $data['ended_at']   = Carbon::parse($request->ended_at);
        $data['pcp']   = implode(',',$data['pcp']);
        return DB::transaction(function() use ($data,$request) {
            $id = $this->create($data)->id;
            $task = $this->find($id);
            //同步总数
            if (!ProjectTaskCount::setCount())
                \Log::info('sync project task count faile');
            //同步应用
            $task->flushApps([$request->app_id]);
            if ($task->save()) {
                return true;
            }
            throw new \Exception('添加记录失败');
        });
    }

    public function rules ($request){
        return [
            'name'       => ['required'/*,Rule::unique($this->table)->ignore($request->id)*/],
            'app_id'     => 'exists:app,id',
            'pcp'        => 'required',
            'started_at' => 'required|date_format:m/d/Y',
            'ended_at'   => 'required|date_format:m/d/Y|after:started_at',
            'status'     =>  Rule::in([self::INTENDING,self::PROCESSING,self::COMPLETED,self::DELETED])
        ];
    }

    public function messages (){
        return [
            'name.required' => '请填写项目名称',
            // 'name.unique'   => '项目名称已存在',
            'app_id.exists' => '请选择目标应用',
            'pcp.required'    => '请选择责任人',
            'started_at.required' => '请选择开始时间',
            'started_at.date_format' => '请选择合法的时间格式',
            'ended_at.required'  => '请选择结束时间',
            'ended_at.date_format' => '请选择合法的时间格式',
            'ended_at.after' => '请选择大于开始的时间',
            'status.in'=>'请勿非法操作'
        ];
    }

    public function validate ($request){
        return Validator::make($request->all(),
            $this->rules($request),
            $this->messages()
        );
    }

    /**
     * SPT-18-001，系统定义，18为年份，后三位递增
     */
    public function generateNumber (){
        $year = Carbon::now()->year;
        $yc = ProjectTaskCount::where(['year'=>$year])->value('count');
        return 'SPT-' . $year . '-' . (intval($yc)+1);
    }

    /**
     * 查询列表
     * @param $sel
     * @param $limit
     */
    public function lists ($sel,$limit){
        $whereBetween = isset($this->requestWhere['startTime']) && !empty($this->requestWhere['startTime']) && isset($this->requestWhere['endTime']) && !empty($this->requestWhere['endTime']);

        $builder = $this->select($sel);
        $builder = $builder->leftjoin('project_task_app', 'project_task_app.project_task_id', '=', $this->table.'.id');
        $builder = $builder->when($whereBetween, function ($query) {
            return $query->whereBetween($this->table.'.created_at',[Carbon::parse($this->requestWhere['startTime']),Carbon::parse($this->requestWhere['endTime'])]);
        })
            ->when($this->whenWhere('pcp'),function($query){
                return $query->where($this->table.'.pcp','=',intval($this->requestWhere['pcp']));
            })
            ->when($this->whenWhere('status'),function($query){
                return $query->where($this->table.'.status','=',intval($this->requestWhere['status']));
            })
            ->when($this->whenWhere('app'),function($query){
                return $query->where('project_task_app.app_id','=',intval($this->requestWhere['app']));
            })
            ->when($this->whenWhere('keywords'),function($query){
                return $query->where($this->table.'.name','like','%'.$this->requestWhere['keywords'].'%');
            })
            ->when($this->whenWhere('id'),function($query){
                if (strpos($this->requestWhere['id'],'!') ===0){
                    return $query->where($this->table.'.id','<>',intval(str_replace('!','',$this->requestWhere['id'])));
                }else {
                    return $query->where($this->table.'.id','==',intval($this->requestWhere['id']));
                }
            });
            $this->builder = $builder->count();
        $list =$builder->offset($limit[0])->limit($limit[1])->orderBy($this->table.'.created_at','DESC')->get();
        foreach ($list as $item){
            //目标应用
            $item->app = App::where(['id'=>$item->app_id])->value('name');
            //责任人
            $pcps = User::whereIn('id',explode(',',$item->pcp))->pluck('name');
            $item->pcp = implode(',',$pcps->toArray());
            //状态
            $item->statusStr = $this->getStrStatus($item->status);
            //漏洞数量
            $item->vul_num = $this->vulNum($item->app_id,[$item->started_at,$item->ended_at])[false];
            //严重漏洞数量
            $item->vul_serious_num = $this->vulNum($item->app_id,[$item->started_at,$item->ended_at])[Level::SERIOUSID];
            //高危漏洞数量
            $item->vul_highrisk_num = $this->vulNum($item->app_id,[$item->started_at,$item->ended_at])[Level::HIGHRISKID];
            //中危漏洞数量
            $item->vul_inrisk_num = $this->vulNum($item->app_id,[$item->started_at,$item->ended_at])[Level::INRISKID];
            //低危漏洞数量
            $item->vul_low_num = $this->vulNum($item->app_id,[$item->started_at,$item->ended_at])[Level::LOWID];
        }
        //条件缓存
        Cache::put('ptwhere-'.Auth::user()->id, $this->requestWhere,10);
        //列表缓存
        Cache::put('ptlist-'.Auth::user()->id, $list,10);
        return $list;
    }

    /**
     * 构造查询条件
     * @param string $where
     * @return array
     */
    public function initWhere (string $where){
       $ret = [];
       if (is_string($where)){
           $arrWhere = explode('&',$where);
           foreach ($arrWhere as $value){
               //兼容<>情况
               if (strpos($value,'!=')){
                   $param = explode('!=',$value);
                   $ret[$param[0]] = '!' .urldecode($param[1]);
               }else{
                   $param = explode('=',$value);
                   $ret[$param[0]] = urldecode($param[1]);
               }
           }
       }
       $this->requestWhere = $ret;
       return $ret;
    }

    /**
     * 构造when条件
     */
    private function whenWhere (string $key){
        if (isset($this->requestWhere[$key])){
            if (!empty($this->requestWhere[$key])){
                return true;
            }
        }
        return false;
    }

    /**
     * 获取总数
     */
    public function getCount(){
        if ($this->builder){
            return $this->builder;
        } else{
            $whereBetween = isset($this->requestWhere['startTime']) && !empty($this->requestWhere['startTime']) && isset($this->requestWhere['endTime']) && !empty($this->requestWhere['endTime']);

            return $this->leftjoin('project_task_app', 'project_task_app.project_task_id', '=', $this->table.'.id')->when($whereBetween, function ($query) {
                return $query->whereBetween($this->table.'.created_at',[Carbon::parse(urldecode($this->requestWhere['startTime'])),Carbon::parse(urldecode($this->requestWhere['endTime']))]);
            })
                ->when($this->whenWhere('pcp'),function($query){
                    return $query->where($this->table.'.pcp','=',intval($this->requestWhere['pcp']));
                })
                ->when($this->whenWhere('status'),function($query){
                    return $query->where($this->table.'.status','=',intval($this->requestWhere['status']));
                })
                ->when($this->whenWhere('app'),function($query){
                    return $query->where('project_task_app.app_id','=',intval($this->requestWhere['app']));
                })
                ->when($this->whenWhere('keywords'),function($query){
                    return $query->where($this->table.'.name','like','%'.$this->requestWhere['keywords'].'%');
                })
                ->when($this->whenWhere('id'),function($query){
                    if (strpos($this->requestWhere['id'],'!') ===0){
                        return $query->where($this->table.'.id','<>',intval(str_replace('!','',$this->requestWhere['id'])));
                    }else {
                        return $query->where($this->table.'.id','==',intval($this->requestWhere['id']));
                    }
                })
                ->count();
        }

    }

    /**
     * 获取状态描述
     * @param int
     * @return string
     */
    public static function getStrStatus(int $status){
        $str = '';
        switch ($status){
            case self::INTENDING;
            $str = '计划中';
            break;
            case self::PROCESSING;
                $str = '进行中';
                break;
            case self::COMPLETED;
                $str = '已完成';
                break;
            case self::DELETED;
                $str = '已删除';
                break;
        }
        return $str;
    }

    /**
     * 根据项目id查询应用
     * @param $id
     * @return mixed
     */
    public static function getAppById ($id){
        $app = null;
        if (intval($id)){

            $where = ['project_task_app.project_task_id'=>$id];
            $app = DB::table('project_task_app')->join('app',function ($join){
                return $join->on('project_task_app.app_id','=','app.id');
            })->where($where)->first();
        }
        if ($app){
            $app->address = str_replace("\r\n",',',$app->address);
            $app->ops     = str_replace("\r\n",',',$app->ops);
            $app->msv     = str_replace("\r\n",',',$app->msv);
            $app->lvv     = str_replace("\r\n",',',$app->lvv);
            $app->ut      = str_replace("\r\n",',',$app->ut);
        }
        return $app;
    }

    /**
     * @param $id
     * @return array
     */
    public static function getAppItem ($id){
        $ret = [];
        if (intval($id)){

            $where = [['project_task_app.project_task_id','=',$id]];
            $ogzPid = DB::table('project_task_app')->join('app',function ($join){
                return $join->on('project_task_app.app_id','=','app.id');
            })->join('ogz',function ($join){
                return $join->on('app.ogz_id','=','ogz.id');
            })->where($where)->value('ogz.id');
            $ogzPid =intval($ogzPid);
            if ($ogzPid>=0){

                $ret = Ogz::getNotTopInfo($ogzPid);
                //去掉集团
                if(count($ret) >1)
                    array_pop($ret);
                $ret = array_reverse($ret);
            }
        }
        return $ret;
    }

    /**
     * 获取流状态
     */
    public static function getStatusArr (){
        return [self::INTENDING=>'计划中',self::PROCESSING=>'进行中',self::COMPLETED=>'已完成',self::DELETED=>'已删除'];
    }

    /**
     * @param $request
     * @return bool
     */
    public function updateRow($request){
        $row = $this->find($request->id);
        $row->name = $request->name;
        $row->pcp = implode(',',$request->pcp);
        $row->started_at = Carbon::parse($request->started_at);
        $row->ended_at = Carbon::parse($request->ended_at);
        $row->status = $request->status;
        if ($row->save())
            return true;
        else
            throw new \Exception('task_id['.$request->id.'] update project task fail');
    }

    /**
     * @param $userIdStr
     * @return string
     */
    public static function getAppUserInfoStr ($userIdStr) {
        $userStr = '';
        $names = User::getNameString(explode(',', $userIdStr));
        $phones = User::getPhoneString(explode(',', $userIdStr));
        $namesArr = !empty($names) ? explode(',', $names) : [];
        $phonesArr = !empty($phones) ? explode(',', $phones) : [];

        foreach ($namesArr as $i => $name) {
            $userStr .= $name . '(' . $phonesArr[$i] . ')，';
        }
        return rtrim($userStr, '，');
    }

    /**
     * @param $tId
     * @param bool $between
     * @return mixed
     */
    public function getBuilderForVul ($tId,$between=true){

        $builder = Vuls::select('vuls.*', 'users.real_name as uname', 'users.name as account', 'level.name as lname', 'app.name as aname', 'category.name as cname', 'users.source', 'app.ogz_id');
        $appId = DB::table('project_task_app')->where(['project_task_id'=>$tId])->value('app_id');
        $betweenTime = ProjectTask::select(['started_at','ended_at'])->where(['id'=>$tId])->first();
        $builder = $builder->leftjoin('users', 'users.id', '=', 'vuls.user_id')
            ->leftjoin('level', 'level.id', '=', 'vuls.level_id')
            ->leftjoin('app', 'app.id', '=', 'vuls.app_id')
            ->leftjoin('category', 'category.id', '=', 'vuls.category_id');
        $builder = $builder->where(['vuls.app_id'=>$appId]);
        //->where('vuls.check_id','!=',0)
        if ($between){

            $builder = $builder->whereBetween('vuls.created_at',[$betweenTime->started_at,$betweenTime->ended_at]);
        }else{
            $builder = $builder->where('vuls.created_at','<=',$betweenTime->started_at);
        }
        return $builder;
    }

    /**
     * 目标应用漏洞数量针对发现个数
     * 不统计历史漏洞个数，项目规划时间之前的
     * @param int $appId
     * @param array $betweenTime
     * @return array
     */
    private function vulNum ($appId,$betweenTime){
        $ret = [false=>0,Level::SERIOUSID=>0,Level::HIGHRISKID=>0,Level::INRISKID=>0,Level::LOWID=>0];
        $list =Vuls::select('id','level_id')->where([['app_id','=',$appId]/*,['check_id','>',0]*/])->whereBetween('created_at',[$betweenTime[0],$betweenTime[1]])->get();
        foreach ($list as $item){
            switch ($item->level_id){
                case Level::SERIOUSID:
                    $ret[Level::SERIOUSID]++;
                    break;
                case Level::HIGHRISKID:
                    $ret[Level::HIGHRISKID]++;
                    break;
                case Level::INRISKID:
                    $ret[Level::INRISKID]++;
                    break;
                case Level::LOWID:
                    $ret[Level::LOWID]++;
                    break;
            }
        }
        $ret[false] = array_sum(array_values($ret));
        return $ret;
    }

}